//======================================================================
//    COPYRIGHT(C) Innobeam
//    ALL RIGHTS RESERVED
//======================================================================
//Filename    : lan9252_interface.v rev 1.0
//Created On  : 20170802
//Author      : shilong.zhang
//Description :	This module write and read 16bit data to lan9252;
//				through HBI bus.(require 10 iClk cycles)
//Include     :
//Modification:
//				IO: assign iovLan9252Ad = (rLan_rd==1'b1)?16'hz:rvLanData;
//=======================================================================

module lan9252_interface
(
	iClk,			// typical 50Mhz
	iRst_n,			// reset active low

	// to LAN9252 protocol(normal,csr,pram,router)
	ivAddr,			// 16bit address (operation destination address)
	iWrEn,			// write enable
	ivData,			// write data, 32bit data to be written to LAN9252
	iRdEn,			// read enable
	ovData,			// read data, 32bit data read from LAN9252
	oDone,			// operation is done

	// to extern LAN9252 chip(HBI bus)
	oLan9252AleL,	// LAN9252 ALELO(HBI bus:address lock enable low)
	oLan9252AleH,	// LAN9252 ALEHI(HBI bus:address lock enable high,not used)
	oLan9252Cs,		// LAN9252 cs(HBI bus:cs)
	oLan9252Rd,		// LAN9252 rd(HBI bus:read)
	oLan9252Wr,		// LAN9252 wr(HBI bus:write)
	ivLan9252Ad,	// 16bit input, LAN9252 ad(HBI bus:address and data input)
	ovLan9252Ad		// 16bit output, LAN9252 ad(HBI bus:address and data output)
);

//==========================================
//    parameter
//==========================================
parameter	DATA_W = 16;
parameter	CNT_NUM = 4'd11;

//==========================================
//    port
//==========================================
input	iClk;	// typical 50Mhz
input	iRst_n;	// reset active low

// to LAN9252 protocol(normal,csr,pram,router)
input 	[DATA_W-1:0]	ivAddr;	// 16bit address (operation destination address)
input 					iWrEn;	// write enable
input 	[2*DATA_W-1:0]	ivData;	// write data, 32bit data to be written to LAN9252
input					iRdEn;	// read enable
output 	[2*DATA_W-1:0]	ovData;	// read data, 32bit data read from LAN9252
output					oDone;	// operation is done

// to extern LAN9252 chip(HBI bus)
output					oLan9252AleL;	// LAN9252 ALELO(HBI bus:address lock enable low)
output					oLan9252AleH;	// LAN9252 ALEHI(HBI bus:address lock enable high,not used)
output					oLan9252Cs;		// LAN9252 cs(HBI bus:cs)
output					oLan9252Rd;		// LAN9252 rd(HBI bus:read)
output					oLan9252Wr;		// LAN9252 wr(HBI bus:write)
input 	[DATA_W-1:0]	ivLan9252Ad;	// 16bit input, LAN9252 ad(HBI bus:address and data input)
output	[DATA_W-1:0]	ovLan9252Ad;	// 16bit output, LAN9252 ad(HBI bus:address and data output)

//==========================================
//    signal
//==========================================
reg 	[DATA_W-1:0]  		rvAddr;
reg 	[2*DATA_W-1:0] 		rvData;
reg		[3:0]				rvCnt;
reg							rAdd_cnt;
reg							rLan_cs;
reg							rLan_wr;
reg							rLan_rd;
reg							rLan_ale;
reg							rWr_Rd_n;
reg		[DATA_W-1:0] 		rvLanData;
reg  	[2*DATA_W-1:0]		rvDataOut;
reg							rDone;
wire						wAdd_cnt;
wire						wEnd_cnt;

//==========================================
//    module body
//==========================================
assign oDone = rDone;
assign ovData = rvDataOut;

assign oLan9252AleH = 1'b0;
assign ovLan9252Ad = rvLanData;
assign oLan9252Cs = rLan_cs;
assign oLan9252Wr = rLan_wr;
assign oLan9252Rd = rLan_rd;
assign oLan9252AleL = rLan_ale;
assign oLan9252AleH = 1'b0;

// when Wr or Rd is valid start counter
// when counter == 4'd11 end counter
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rAdd_cnt <= 1'b0;
	rWr_Rd_n <= 1'b0;
	rvAddr <= {DATA_W{1'b0}};
	rvData <= {(2*DATA_W){1'b0}};
	end
else begin
	if(iWrEn == 1'b1)begin
		rAdd_cnt <= 1'b1;
		rWr_Rd_n <= 1'b1;
		rvAddr <= ivAddr;
		rvData <= ivData;
		end
	else if(iRdEn ==1'b1)begin
		rAdd_cnt <= 1'b1;
		rWr_Rd_n <= 1'b0;
		rvAddr <= ivAddr;
		rvData <= rvData;
		end
	else if(wEnd_cnt == 1'b1)begin
		rAdd_cnt <= 1'b0;
		rWr_Rd_n <= rWr_Rd_n;
		rvAddr <= rvAddr;
		rvData <= rvData;
		end
	else begin
		rAdd_cnt <= rAdd_cnt;
		rWr_Rd_n <= rWr_Rd_n;
		rvAddr <= rvAddr;
		rvData <= rvData;
		end
	end
end

// counter
assign wAdd_cnt = rAdd_cnt;
assign wEnd_cnt = (wAdd_cnt == 1'b1) && (rvCnt == CNT_NUM);

always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rvCnt <= 0;
	end
else if(wAdd_cnt == 1'b1)begin
	if(wEnd_cnt == 1'b1)begin
		rvCnt <= 0;
		end
	else begin
		rvCnt <= rvCnt + 1'b1;
		end
	end
end

// lan9252ale output
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rLan_ale <= 1'b0;
	end
else begin
	case({wAdd_cnt,rWr_Rd_n,rvCnt})
	6'b1_0_0000,6'b1_1_0000:begin // rvCnt==0,wAdd_cnt==1
		rLan_ale <= 1'b1;
		end
	6'b1_1_0001,6'b1_0_0001:begin // rvCnt==1
		rLan_ale <= 1'b0;
		end
	6'b1_1_0101:begin // rvCnt==5,wr == 1
		rLan_ale <= 1'b1;
		end
	6'b1_1_0110:begin // rvCnt==6,wr == 1
		rLan_ale <= 1'b0;
		end
	6'b1_0_0110:begin // rvCnt==6,rd == 1
		rLan_ale <= 1'b1;
		end
	6'b1_0_0111:begin // rvCnt==7,rd == 1
		rLan_ale <= 1'b0;
		end
	default:begin
		rLan_ale <= rLan_ale;
		end
	endcase
	end
end

// lan9252cs output
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rLan_cs <= 1'b0;
	end
else begin
	case({wAdd_cnt,rWr_Rd_n,rvCnt})
	6'b1_0_0000,6'b1_1_0000:begin // rvCnt==0,wAdd_cnt==1
		rLan_cs <= 1'b1;
		end
	6'b1_1_0100:begin // rvCnt==4,wr == 1
		rLan_cs <= 1'b0;
		end
	6'b1_1_0101:begin // rvCnt==5,wr == 1
		rLan_cs <= 1'b1;
		end
	6'b1_1_1001:begin // rvCnt==9,wr == 1
		rLan_cs <= 1'b0;
		end
	6'b1_0_0101:begin // rvCnt==5,rd == 1
		rLan_cs <= 1'b0;
		end
	6'b1_0_0110:begin // rvCnt==6,rd == 1
		rLan_cs <= 1'b1;
		end
	6'b1_0_1011:begin // rvCnt==11,rd == 1
		rLan_cs <= 1'b0;
		end
	default:begin
		rLan_cs <= rLan_cs;
		end
	endcase
	end
end

// lan9252wr output
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rLan_wr <= 1'b0;
	end
else begin
	case({rWr_Rd_n,rvCnt})
	5'b1_0010,5'b1_0111:begin // rvCnt==2,7
		rLan_wr <= 1'b1;
		end
	5'b1_0100,5'b1_1001:begin // rvCnt==4,9
		rLan_wr <= 1'b0;
		end
	default:begin
		rLan_wr <= rLan_wr;
		end
	endcase
	end
end

// lan9252rd output
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rLan_rd <= 1'b0;
	end
else begin
	case({rWr_Rd_n,rvCnt})
	5'b0_0010,5'b0_1000:begin // rvCnt==2,8
		rLan_rd <= 1'b1;
		end
	5'b0_0101,5'b0_1011:begin // rvCnt==5,11
		rLan_rd <= 1'b0;
		end
	default:begin
		rLan_rd <= rLan_rd;
		end
	endcase
	end
end

// lan9252ad output(write operation)
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rvLanData <= {DATA_W{1'b0}};
	end
else begin
	case({wAdd_cnt,rWr_Rd_n,rvCnt})
	6'b1_0_0000,6'b1_1_0000:begin // rvCnt==0,wr_rd==x,wAdd_cnt==1
		rvLanData <= rvAddr;
		end
	6'b0_1_0010,6'b1_1_0010:begin // rvCnt==2,wr==1,wAdd_cnt==x
		rvLanData <= rvData[15:0];                             
		end                                                    
	6'b0_1_0101,6'b1_1_0101:begin // rvCnt==5,wr==1,wAdd_cnt==x
		rvLanData <= rvAddr + 1'b1;                            
		end                                                    
	6'b0_1_0111,6'b1_1_0111:begin // rvCnt==7,wr==1,wAdd_cnt==x
		rvLanData <= rvData[31:16];                            
		end                                                    
	6'b1_0_0110,6'b0_0_0110:begin // rvCnt==6,rd ==1,wAdd_cnt==x
		rvLanData <= rvAddr + 1'b1;
		end
	default:begin
		rvLanData <= rvLanData;
		end
	endcase
	end
end

// lan9252ad input(read operation)
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rvDataOut <= {(2*DATA_W){1'b0}};
	end
else begin
	case({rWr_Rd_n,rvCnt})
	5'b0_0101:begin // rvCnt==5,rd==1
		rvDataOut <= {rvDataOut[31:16],ivLan9252Ad};
		end
	5'b0_1011:begin // rvCnt==11,rd==1
		rvDataOut <= {ivLan9252Ad,rvDataOut[15:0]};
		end
	default:begin
		rvDataOut <= rvDataOut;
		end
	endcase
	end
end

// lan9252_done output
always @(posedge iClk or negedge iRst_n)begin
if(iRst_n == 1'b0)begin
	rDone <= 1'b0;
	end
else begin
	case({rWr_Rd_n,rvCnt})
	5'b1_1001:begin // rvCnt==9,wr==1
		rDone <= 1'b1;
		end
	5'b0_1011:begin // rvCnt==11,rd==1
		rDone <= 1'b1;
		end
	default:begin
		rDone <= 1'b0;
		end
	endcase
	end
end

endmodule

